package com.rephilo.factory;

import com.rephilo.ComponentScanConfig;
import com.rephilo.annotation.Autowired;
import com.rephilo.annotation.ComponentScan;
import com.rephilo.annotation.Service;
import com.rephilo.utils.CommonUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 工厂类，生产对象
 * 读取包路径下所有的类，解析所有带Service注解的类，通过反射技术实例化对象并且存储待用（map集合）
 *
 * @author rephilo
 */
public class BeanFactory {
    /**
     * 单例池
     */
    private static Map<String, Object> SINGLETON_MAP = new HashMap<>();
    /**
     * 二级缓存map
     */
    private static Map<String, Object> EARLY_MAP = new HashMap<>();
    /**
     * 类型-beanId map
     */
    private static Map<Class, String> CLASSES_BEANS_MAP = new HashMap<>();
    /**
     * 代理工厂-用来创建bean
     */
    private static ProxyFactory PROXY_FACTORY = new ProxyFactory();

    /**
     * 初始化方法
     *
     * @param componentScanConfigClass
     */
    public static void init(Class<ComponentScanConfig> componentScanConfigClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取扫描的路径
        String packageName = componentScanConfigClass.getAnnotation(ComponentScan.class).value();

        //用反射工具扫描该路径
        Reflections reflections = new Reflections(packageName);

        //获取所有有Service注解的类
        Set<Class<?>> serviceClasses = reflections.getTypesAnnotatedWith(Service.class);

        //将扫描到的所有有注解的类实例化
        for (Class<?> serviceClass : serviceClasses) {
            createBean(serviceClass);
        }

        //执行依赖注入
        for (Class<?> serviceClass : serviceClasses) {
            //生成beanId
            String beanId = serviceClass.getAnnotation(Service.class).value();
            if (StringUtils.isBlank(beanId)) {
                beanId = CommonUtils.lowerCaseFirstLetter(serviceClass.getSimpleName());
            }

            Object currentEarlyObject = EARLY_MAP.get(beanId);

            for (Field field : currentEarlyObject.getClass().getDeclaredFields()) {
                //找到有Autowired的属性
                if (field.getAnnotation(Autowired.class) != null) {
                    Class<?> type = field.getType();

                    //寻找可用的bean
                    String fieldBeanId = CLASSES_BEANS_MAP.get(type);
                    if (StringUtils.isNotBlank(fieldBeanId)) {
                        //先查单例池
                        Object fieldsBean = SINGLETON_MAP.get(fieldBeanId);
                        if (fieldsBean == null) {
                            //再查二级缓存
                            fieldsBean = EARLY_MAP.get(fieldBeanId);
                        }

                        //执行注入
                        field.setAccessible(true);
                        field.set(currentEarlyObject, fieldsBean);
                    }
                }
            }

            //完成注入之后，将自己放到单例池中，并且移出二级缓存
            SINGLETON_MAP.put(beanId, currentEarlyObject);
            EARLY_MAP.remove(beanId);
        }

        //完成注入后，对每个类生成代理对象
        for (Map.Entry<String, Object> entry : SINGLETON_MAP.entrySet()) {
            Object beanProxy = createBeanProxy(entry.getValue());
            entry.setValue(beanProxy);
        }
    }

    /**
     * 处理Bean实例化过程
     *
     * @param serviceClass
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    private static void createBean(Class<?> serviceClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //生成beanId
        String beanId = serviceClass.getAnnotation(Service.class).value();
        if (StringUtils.isBlank(beanId)) {
            beanId = serviceClass.getSimpleName();
            beanId = CommonUtils.lowerCaseFirstLetter(beanId);
        }

        //实例化代理对象
        Object bean = serviceClass.getDeclaredConstructor().newInstance();

        //先放到二级缓存中
        EARLY_MAP.putIfAbsent(beanId, bean);

        //将类或接口 和 beanId的映射保存一份
        Class<?>[] interfaces = serviceClass.getInterfaces();
        if (interfaces.length > 0) {
            for (Class<?> interfaceClass : interfaces) {
                CLASSES_BEANS_MAP.put(interfaceClass, beanId);
            }
        }
        CLASSES_BEANS_MAP.put(serviceClass, beanId);
    }

    /**
     * 对外提供获取实例对象的接口（根据id获取）
     *
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return SINGLETON_MAP.get(id);
    }

    /**
     * 实例化bean的代理对象
     *
     * @param bean
     * @return proxyBean
     */
    private static Object createBeanProxy(Object bean) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> beanClass = bean.getClass();
        Class<?>[] interfaces = beanClass.getInterfaces();
        //有接口的使用jdk动态代理
        if (interfaces.length > 0) {
            return PROXY_FACTORY.getJdkProxy(bean);
        }

        //否则使用cglib动态代理
        return PROXY_FACTORY.getCglibProxy(bean);
    }
}

